package com.xfzp.d3;

import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;
import java.nio.ShortBuffer;

/**
 * 形状。每个形状由一到多个三角形组成。
 * 
 * @author AndroidApe
 * @version 2011-11-9
 */
public class Shape {
	private Vertex[] vs;
	private short size;

	private ShortBuffer indexBuffer;
	private FloatBuffer vertexBuffer;
	private FloatBuffer colorBuffer;

	private ShortBuffer indexBufferReadOnly;
	private FloatBuffer vertexBufferReadOnly;
	private FloatBuffer colorBufferReadOnly;

	/**
	 * 构造形状
	 * 
	 * @param size
	 *            此形状的顶点个数
	 * @param index
	 *            组成此形状的所有三角形的顶点顺序数组
	 */
	public Shape(short size, short[] index) {
		this.size = size;
		vs = new Vertex[size];
		initBuffer(index);
	}

	/**
	 * 返回组成此形状的所有三角形的顶点顺序的缓存。返回的缓存为只读缓存、并且位置为0
	 * 
	 * @return
	 */
	public ShortBuffer indexBuffer() {
		indexBufferReadOnly.position(0);
		return indexBufferReadOnly;
	}

	/**
	 * 返回顶点的缓存。返回的缓存为只读缓存、并且位置为0<br/>
	 * 只有通过set方法设置或修改顶点或者multiply重计算顶点的坐标时，才会同步更新缓存，因此在外部更新顶点的坐标后必须调用set方法修改顶点
	 * 
	 * @return
	 */
	public FloatBuffer vertexBuffer() {
		vertexBufferReadOnly.position(0);
		return vertexBufferReadOnly;
	}

	/**
	 * 返回顶点的颜色缓存。返回的缓存为只读缓存、并且位置为0<br/>
	 * 只有通过set方法设置或修改顶点时，才会同步更新缓存，因此在外部更新顶点的颜色后必须调用set方法修改顶点
	 * 
	 * @return
	 */
	public FloatBuffer colorBuffer() {
		colorBufferReadOnly.position(0);
		return colorBufferReadOnly;
	}

	/**
	 * 从0处开始设置顶点
	 * 
	 * @param vs
	 * @return
	 */
	public Shape set(Vertex... vs) {
		int len = Math.min(size, vs.length);
		for (short i = 0; i < len; i++) {
			set(i, vs[i]);
		}
		return this;
	}

	/**
	 * 设置指定位置的顶点
	 * 
	 * @param i
	 * @param v
	 * @return
	 */
	public Shape set(int i, Vertex v) {
		vs[i] = v;
		if (v != null) {
			vertexBuffer.position(i * 3);
			vertexBuffer.put(v.x());
			vertexBuffer.put(v.y());
			vertexBuffer.put(v.z());
			Color c = v.c();
			if (c != null) {
				colorBuffer.position(i * 4);
				colorBuffer.put(c.r);
				colorBuffer.put(c.g);
				colorBuffer.put(c.b);
				colorBuffer.put(c.a);
			}
		}
		return this;
	}

	/**
	 * 设置指定位置的顶点的颜色
	 * 
	 * @param c
	 * @param indexs
	 * @return
	 */
	public Shape set(Color c, int... indexs) {
		for (int i : indexs) {
			if (vs[i] != null) {
				vs[i].setColor(c);
				if (c != null) {
					colorBuffer.position(i * 4);
					colorBuffer.put(c.r);
					colorBuffer.put(c.g);
					colorBuffer.put(c.b);
					colorBuffer.put(c.a);
				}
			}
		}
		return this;
	}

	/**
	 * 更新顶点缓存和颜色缓存
	 */
	public void updateBuffer() {
		vertexBuffer.position(0);
		colorBuffer.position(0);
		for (Vertex v : vs) {
			if (v != null) {
				vertexBuffer.put(v.x());
				vertexBuffer.put(v.y());
				vertexBuffer.put(v.z());
				Color c = v.c();
				if (c != null) {
					colorBuffer.put(c.r);
					colorBuffer.put(c.g);
					colorBuffer.put(c.b);
					colorBuffer.put(c.a);
				} else {
					colorBuffer.position(colorBuffer.position() + 4);
				}
			} else {
				vertexBuffer.position(vertexBuffer.position() + 3);
				colorBuffer.position(colorBuffer.position() + 4);
			}
		}
	}

	/**
	 * 返回顶点
	 * 
	 * @param i
	 * @return
	 */
	public Vertex v(int i) {
		return vs[i];
	}

	/**
	 * 返回顶点个数
	 * 
	 * @return
	 */
	public short size() {
		return size;
	}

	/**
	 * 乘以一个4 x 4的矩阵
	 * 
	 * @param m
	 */
	public void multiply(float[] m) {
		vertexBuffer.position(0);
		for (Vertex v : vs) {
			if (v != null) {
				v.multiply(m);
				vertexBuffer.put(v.x());
				vertexBuffer.put(v.y());
				vertexBuffer.put(v.z());
			} else {
				vertexBuffer.position(vertexBuffer.position() + 3);
			}
		}
	}

	private void initBuffer(short[] index) {
		ByteBuffer b = ByteBuffer.allocateDirect(index.length * 2);
		b.order(ByteOrder.nativeOrder());
		indexBuffer = b.asShortBuffer();// ShortBuffer.allocate(index.length);
		indexBuffer.put(index);
		indexBufferReadOnly = indexBuffer.asReadOnlyBuffer();
		b = ByteBuffer.allocateDirect(size * 12);
		b.order(ByteOrder.nativeOrder());
		vertexBuffer = b.asFloatBuffer();// FloatBuffer.allocate(size * 3);
		vertexBufferReadOnly = vertexBuffer.asReadOnlyBuffer();
		b = ByteBuffer.allocateDirect(size * 16);
		b.order(ByteOrder.nativeOrder());
		colorBuffer = b.asFloatBuffer();// FloatBuffer.allocate(size * 4);
		colorBufferReadOnly = colorBuffer.asReadOnlyBuffer();
	}
}
